﻿using System;
using System.Collections.Generic;
using System.Linq;

using btmerc.API.Schemas;
using btmerc.API.Schemas.Interfaces;

using btmerc.API.Enums;

namespace btmerc.API
{

    public class DragoonsRating
    {
        #region Private Fields
        private short _dragoonsRating = 0;
        private ushort _experiencePoints = 0;
        private short _commanderPoints = 0;
        private int _recordPoints = 0;
        private ushort _transportPoints = 0;
        private ushort _techPoints = 0;
        private ushort _supportPoints = 0;
        private ushort _medicalPoints = 0;
        private ushort _adminPoints = 0;
        private short _financialPoints = 0;

        internal IForce _force;
        #endregion

        public short Score
        {
            get
            {
                return _dragoonsRating;
            }
        }

        public Rating Rating
        {
            get
            {
                if (_dragoonsRating < 0)
                {
                    return Schemas.Rating.F;
                }
                if (_dragoonsRating < 46)
                {
                    return Schemas.Rating.D;
                }
                if (_dragoonsRating < 86)
                {
                    return Schemas.Rating.C;
                }
                if (_dragoonsRating < 121)
                {
                    return Schemas.Rating.B;
                }
                if (_dragoonsRating < 151)
                {
                    return Schemas.Rating.A;
                }
                else
                {
                    throw new NotImplementedException();
                }
            }
        }

        public DragoonsRating(IForce force)
        {
            _force = force;
            RecalculateRating();
        }

        public void RecalculateRating()
        {
            CalculateExperienceRating();
            CalculateCommanderRating();
            CalculateRecordRating();
            CalculateTransportRating();
            CalculateTechnologyRating();
            CalculateSupportRating();
            CalculateMedicalRating();
            CalculateAdminRating();
            CalculateFinancialRating();
            CalculateRating();
        }

        private void CalculateExperienceRating()
        {
            switch (_force.GetAverageCombatQuality())
            {
                case Quality.Elite:
                    _experiencePoints = 40;
                    break;
                case Quality.Veteran:
                    _experiencePoints = 20;
                    break;
                case Quality.Regular:
                    _experiencePoints = 10;
                    break;
                case Quality.Green:
                    _experiencePoints = 5;
                    break;
                default:
                    _experiencePoints = 0;
                    break;
            }
        }

        private void CalculateCommanderRating()
        {
            _commanderPoints = 0;
            foreach (Trait trait in _force.Commander.Traits.Trait)
            {
                if (trait.Name == "Brave" ||
                    trait.Name == "Combat Sense" ||
                    trait.Name == "Contact" ||
                    trait.Name == "Good Reputation" ||
                    trait.Name == "Land Grant" ||
                    trait.Name == "Title" ||
                    trait.Name == "Wealth" ||
                    trait.Name == "Well-Connected")
                {
                    _commanderPoints++;
                }
                else if(trait.Name == "Bad Reputation" ||
                        trait.Name == "Combat Paralysis" ||
                        trait.Name == "Madness" ||
                        trait.Name == "Timid" ||
                        trait.Name == "Unlucky")
                {
                    _commanderPoints--;
                }
            }
            foreach (Skill skill in _force.Commander.Skills.Skill)
            {
                if (skill.Name == "Leadership" ||
                    skill.Name == "Tactics" ||
                    skill.Name == "Strategy" ||
                    skill.Name == "Negotiation")
                {
                    _commanderPoints += (short)skill.Value;
                }
            }

            if (_force.Commander.Attributes.Charisma >= 7)
            {
                _commanderPoints++;
            }
            else if (_force.Commander.Attributes.Charisma <= 3)
            {
                _commanderPoints--;
            }
            if (_force.Commander.Attributes.Social >= 7)
            {
                _commanderPoints++;
            }
            else if (_force.Commander.Attributes.Social <= 3)
            {
                _commanderPoints--;
            }
        }

        private void CalculateRecordRating()
        {
            var contracts = _force.GetContractHistory();
            var longTermContracts = contracts.Where(ec => ec.ContractTerms.ContractType == ContractType.GarrisonDuty || 
                                                          ec.ContractTerms.ContractType == ContractType.Retainer);
            var shortTermContracts = contracts.Except(longTermContracts);

            int successfulDuration = longTermContracts.Where(lt => lt.ContractResult == ContractResult.Successful)
                                                      .Sum(ec => ec.ContractTerms.ContractTerms.Duration);
            _recordPoints = (((successfulDuration/3)*5) +
                              (shortTermContracts.Where(st => st.ContractResult == ContractResult.Successful).Count() * 5) +
                              (_force.GetContractResultCount(ContractResult.Failure) * -10) +
                              (_force.GetContractResultCount(ContractResult.BreachOfContract) * -25));
        }

        private void CalculateTransportRating()
        {
            // TODO: factor jumpships and warships in
            _transportPoints = 0;
            TroopCount troopCount = _force.GetTroopTransportNeeded();
            TroopCount totalVehicleCapacity = _force.GetTotalTroopTransportCapacity();
            TroopCount uncoveredTroops = new TroopCount(troopCount, totalVehicleCapacity);

            if (troopCount.HasTroops())
            {
                double capacity = 1 - (uncoveredTroops.CombinedTroopCount / (double)troopCount.CombinedTroopCount);
                float tens = (float)Math.Truncate(capacity * 10);
                if (tens > 5)
                {
                    _transportPoints += (ushort)(10 * (tens - 5));
                }
            }
            //_transportPoints += (ushort)(_force.Jumpships.Count() * 10);
        }

        private void CalculateTechnologyRating()
        {
            _techPoints = 0;
            var vehicles = _force.GetVehicles();
            if (vehicles.Any())
            {
                int level2 = vehicles.Where(v => v.GetHighestTechBase() == TechBase.IS2).Count();
                int clan = vehicles.Where(v => v.GetHighestTechBase() == TechBase.Clan).Count();
                float percentage = (float)(level2 + clan + clan) / vehicles.Count();
                percentage *= 10;
                percentage = (float)Math.Truncate(percentage);
                if (percentage > 3)
                {
                    _techPoints += (ushort)((percentage - 3) * 10);
                }
            }
        }

        private void CalculateSupportRating()
        {
            _supportPoints = 0;
            ushort bmTechNeeded = (ushort)_force.GetTechnicalSupportNeeded(TroopType.Battlemech);
            ushort aeroTechNeeded = (ushort)_force.GetTechnicalSupportNeeded(TroopType.AerospaceFighter);
            ushort vehicleTechNeeded = (ushort)_force.GetTechnicalSupportNeeded(TroopType.Vehicle);
            ushort bmTech = _force.GetTechnicalSupportAvailable(TroopType.Battlemech);
            ushort aeroTech = _force.GetTechnicalSupportAvailable(TroopType.AerospaceFighter);
            ushort vehicleTech = _force.GetTechnicalSupportAvailable(TroopType.Vehicle);

            if (bmTech > bmTechNeeded)
            {
                bmTech = bmTechNeeded;
            }
            if (aeroTech > aeroTechNeeded)
            {
                aeroTech = aeroTechNeeded;
            }
            if (vehicleTech > vehicleTechNeeded)
            {
                vehicleTech = vehicleTechNeeded;
            }

            float techPercentage = ((float)bmTech + aeroTech + vehicleTech)/(bmTechNeeded + aeroTechNeeded + vehicleTechNeeded);
            techPercentage = (float)Math.Truncate(techPercentage * 10);
            if (techPercentage > 6)
            {
                _supportPoints += (ushort)((techPercentage - 6) * 5);
            }
        }

        private void CalculateMedicalRating()
        {
            _medicalPoints = 0;
            ushort medicalAvailable = _force.GetMedicalSupportAvailable();
            ushort medicalNeed = _force.GetMedicalSupportNeeded();
            if (medicalAvailable > medicalNeed)
            {
                medicalAvailable = medicalNeed;
            }
            float medPercentage = ((float)medicalAvailable)/medicalNeed;
            medPercentage = (float)Math.Truncate(medPercentage * 100);
            if (medPercentage > 75)
            {
                _medicalPoints += (ushort)(2 * ((medPercentage - 75) / 5));
            }
        }

        private void CalculateAdminRating()
        {
            _adminPoints = 0;
            ushort adminAvailable = _force.GetAdminSupportAvailable();
            ushort adminNeed = _force.GetAdminSupportNeeded();
            if (adminAvailable > adminNeed)
            {
                adminAvailable = adminNeed;
            }
            float adminPercentage = ((float)adminAvailable) / adminNeed;
            adminPercentage = (float)Math.Truncate(adminPercentage * 10);
            if (adminPercentage > 6)
            {
                _adminPoints += (ushort)(adminPercentage - 6);
            }
        }

        private void CalculateFinancialRating()
        {
            _financialPoints = 0;
            ushort debtLength = _force.GetLengthOfDebt();
            if (debtLength > 0)
            {
                _financialPoints -= (short)(10 * ((debtLength / 12) + 1));
            }
        }

        private void CalculateRating()
        {
            _dragoonsRating = (short)(_experiencePoints + _commanderPoints + 
                _recordPoints + _transportPoints + _techPoints + _supportPoints +
                _medicalPoints + _adminPoints + _financialPoints);
        }
    }
}
